#!/bin/sh

check_backup() {
  local NODE="${1:-0}"

  check_wal_archive "$NODE"

  check_automatic_backup "$NODE"

  check_timelines

  check_control_data
}

check_wal_archive() {
  local NODE="${1:-0}"
  local RESULT
  local EXIT_CODE
  try_function wait_until try_check_wal_archive "$NODE"
  if "$RESULT"
  then
    success "The WAL is available"
  else
    fail "The WAL is not available"
  fi
}

try_check_wal_archive() {
  local NODE="${1:-0}"
  local CURRENT_WAL_FILE
  CURRENT_WAL_FILE="$(rotate_wal_file "$1")"
  timeout -s KILL "$((E2E_TIMEOUT / 20))" \
    kubectl exec -n "$CLUSTER_NAMESPACE" "$CLUSTER_NAME"-"$NODE" -c patroni -- \
    exec-with-env backup -- wal-g wal-fetch "$CURRENT_WAL_FILE" "/tmp/$CURRENT_WAL_FILE"
}

rotate_wal_file() {
  local NODE="${1:-0}"
  local CURRENT_WAL_FILE
  CURRENT_WAL_FILE="$(get_current_wal_file "$1")"
  wait_until eval '[ "$(kubectl exec -n "$CLUSTER_NAMESPACE" "$CLUSTER_NAME"-"$NODE" -c postgres-util -- \
      psql -q -t -A -p 5432 \
      -c "CHECKPOINT" \
      -c "SELECT r.file_name from pg_walfile_name_offset(pg_switch_wal()) as r")" != "$CURRENT_WAL_FILE" ]' >&2
  printf '%s' "$CURRENT_WAL_FILE"
}

get_current_wal_file() {
  local NODE="${1:-0}"
  kubectl exec -n "$CLUSTER_NAMESPACE" "$CLUSTER_NAME"-"$NODE" -c postgres-util -- \
    psql -q -t -A -p 5432 -c \
    'SELECT r.file_name from pg_walfile_name_offset(pg_current_wal_lsn()) as r'
}

check_automatic_backup() {
  local NODE="${1:-0}"

  kubectl delete job -n "$CLUSTER_NAMESPACE" -l "stackgres.io/scheduled-backup=true" --ignore-not-found
  kubectl delete sgbackup -n "$CLUSTER_NAMESPACE" --all

  if wait_until eval '[ "$(kubectl get sgbackup -n "$CLUSTER_NAMESPACE" -o name | wc -l)" -gt 0 ]'
  then
    success "The automatic backup CR is running"
  else
    fail "The automatic backup CR is not running"
  fi

  if wait_until eval '[ "$(kubectl get sgbackup -n "$CLUSTER_NAMESPACE" \
    --template "{{ range .items }}{{ .status.process.status }} {{ .status.backupInformation.sourcePod }}{{ print \"\n\" }}{{ end }}" \
    | grep "^Completed ${CLUSTER_NAME}-${NODE}$" | wc -l)" -gt 0 ]'
  then
    success "The automatic backup CR has completed"
  else
    fail "The automatic backup CR has failed"
  fi
}

check_timelines(){
  local BACKUP_LINE
  local EXPECTED_TIMELINE
  local ACTUAL_TIMELINE
  BACKUP_LINE="$(kubectl get sgbackup -n "$CLUSTER_NAMESPACE" \
    --template '{{ range .items }}{{ .status.process.status }} {{ .status.backupInformation.startWalFile }} {{ .status.backupInformation.timeline }}{{ print "\n" }}{{ end }}' \
    | grep "^Completed " | tail -n 1)"

  EXPECTED_TIMELINE="$(echo "$BACKUP_LINE" | cut -d ' ' -f 2)"
  EXPECTED_TIMELINE="$(printf "%d" 0x$(expr substr "$EXPECTED_TIMELINE" 1 8))"
  ACTUAL_TIMELINE="$(echo "$BACKUP_LINE" | cut -d ' ' -f 3)"

  if [ "$EXPECTED_TIMELINE" != "$ACTUAL_TIMELINE" ]
  then
   fail "Timeline of $(echo "$BACKUP_LINE" | cut -d ' ' -f 1) with walFile $(echo "$BACKUP_LINE" | cut -d ' ' -f 3) is incorrect. Actual timeline: $ACTUAL_TIMELINE"
  else
    echo "Backup $(echo "$BACKUP_LINE" | cut -d ' ' -f 1) timeline is correct"
  fi
}

check_control_data(){
  local EXPECTED_CONTROL_DATA_LENGTH
  EXPECTED_CONTROL_DATA_LENGTH="$(kubectl exec -n "$CLUSTER_NAMESPACE" "$CLUSTER_NAME-0" -c patroni \
    -- bash -c 'pg_controldata --pgdata=$PG_DATA_PATH' | wc -l)"
  kubectl get sgbackup -n "$CLUSTER_NAMESPACE" \
    --template '{{ range .items }}{{ .metadata.name }} {{ .status.process.status }}{{ print "\n" }}{{ end }}' \
    | grep " Completed$" \
    | while read BACKUP_NAME STATUS
      do
        local ACTUAL_CONTROL_DATA_LENGTH
        ACTUAL_CONTROL_DATA_LENGTH="$(kubectl get sgbackup -n "$CLUSTER_NAMESPACE" "$BACKUP_NAME" -o json \
          | jq '.status.backupInformation.controlData | length')"

        if [ "$EXPECTED_CONTROL_DATA_LENGTH" != "$ACTUAL_CONTROL_DATA_LENGTH" ]
        then
          fail "Backup $BACKUP_NAME has invalid controlData. Expected $EXPECTED_CONTROL_DATA_LENGTH fields, found $ACTUAL_CONTROL_DATA_LENGTH"
        else
          echo "Backup $BACKUP_NAME has valid controlData"
        fi
      done
}

check_automatic_backup_cr() {
  local NODE="${1:-0}"

  if wait_until is_automatic_backup_cr_completed "$NODE"
  then
    success "The full backup is available"
  else
    fail "The full backup is not available"
  fi
}

is_automatic_backup_cr_completed() {
  local NODE="${1:-0}"
  local BACKUP_NAME

  BACKUP_NAME="$(kubectl get sgbackup -n "$CLUSTER_NAMESPACE" \
    --template '{{ range .items }}{{ .status.internalName }} {{ .status.process.status }} {{ .status.backupInformation.sourcePod }}{{ print "\n" }}{{ end }}' \
    | grep " Completed ${CLUSTER_NAME}-${NODE}$" | tail -n 1 | cut -d ' ' -f 1)"

  if [ "$(kubectl exec -n "$CLUSTER_NAMESPACE" "${CLUSTER_NAME}-${NODE}" -c patroni -- \
      exec-with-env backup -- wal-g backup-list 2>/dev/null | grep "^$BACKUP_NAME " | wc -l)" -gt 0 ]
  then
    success "The automatic backup is available"
  else
    fail "The automatic backup is not available"
  fi
}

wait_backup_is_completed() {
  local BACKUP_NAME="$1"
  local NODE="${2:-0}"
  if wait_until eval '[ "$(kubectl get sgbackup -n "$CLUSTER_NAMESPACE" "$BACKUP_NAME" \
    --template "{{ .status.process.status }} {{ .status.backupInformation.sourcePod }}" \
    | grep "^Completed ${CLUSTER_NAME}-${NODE}$" | wc -l)" -gt 0 ]'
  then
    success "The manual backup CR has complete"
  else
    fail "The manual backup CR has failed"
  fi
}

create_backup() {
  cat << EOF | kubectl create -f -
apiVersion: stackgres.io/v1
kind: SGBackup
metadata:
  namespace: "$CLUSTER_NAMESPACE"
  name: "$1"
spec:
  sgCluster: "$CLUSTER_NAME"
  managedLifecycle: $2
EOF
}

check_is_managed_lifecycle_value() {
  local BACKUP_NAME="$1"
  local EXPECTED_MANAGED_LYFECYCLE="$2"
  local ACTUAL_MANAGED_LYFECYCLE
  ACTUAL_MANAGED_LYFECYCLE="$(kubectl get sgbackup -n "$CLUSTER_NAMESPACE" "$BACKUP_NAME" \
    --template '{{ .status.process.managedLifecycle }} {{ .status.process.status }} {{ .status.backupInformation.sourcePod }}' \
    | grep " Completed ${CLUSTER_NAME}-${NODE}$" | tail -n 1 | cut -d ' ' -f 1)"

  if [ "$ACTUAL_MANAGED_LYFECYCLE" != "$EXPECTED_MANAGED_LYFECYCLE" ]
  then
    fail "Backup /status/process/managedLifecycle expected to be $EXPECTED_MANAGED_LYFECYCLE but was $ACTUAL_MANAGED_LYFECYCLE"
  else
    success "Backup /status/process/managedLifecycle was $EXPECTED_MANAGED_LYFECYCLE"
  fi
}

check_backup_retention() {
  local NODE="${1:-0}"

  if wait_until eval '[ "$(kubectl get sgbackup -n "$CLUSTER_NAMESPACE" \
    --template "{{ range .items }}{{ .status.process.status }} {{ .status.backupInformation.sourcePod }}{{ print \"\n\" }}{{ end }}" \
    | grep "^Completed ${CLUSTER_NAME}-${NODE}$" | wc -l)" -ge 2 ]'
  then
    success "The maximum retainable backups are available"
  else
    fail "The maximum retainable backups are not available"
  fi

  local BACKUP_NAME
  BACKUP_NAME="$(kubectl get sgbackup -n "$CLUSTER_NAMESPACE" -o json)"
  BACKUP_NAME="$(printf %s "$BACKUP_NAME" \
    | jq -r '.items | sort_by(.metadata.creationTimestamp)[]
      | select(
        .status != null and .status.process != null
        and .status.backupInformation.sourcePod != null
        and .status.process.status == "Completed"
        and .status.backupInformation.sourcePod == "'"${CLUSTER_NAME}-${NODE}"'"
        ).metadata.name')"
  BACKUP_NAME="$(printf %s "$BACKUP_NAME" | head -n 1)"

  if wait_until -t "$((E2E_TIMEOUT * 2))" eval '! kubectl get sgbackup -n "$CLUSTER_NAMESPACE" "$BACKUP_NAME" > /dev/null 2>&1'
  then
    success "The backup retention has been honored. Old backups $BACKUP_NAME deleted"
  else
    fail_no_return "The backup retention has not been honored. Old backups $BACKUP_NAME not deleted"
    kubectl get sgbackup -n "$CLUSTER_NAMESPACE"
    return 1
  fi

  if wait_until eval '[ "$(get_completed_managed_backup_count)" = "2" ]'
  then
    success "The backup retention has been honored. Remaining backups are 2"
  else
    fail "The backup retention has not been honored. Remaining backups are $(get_completed_managed_backup_count)"
  fi
}

get_completed_managed_backup_count() {
  kubectl get sgbackup -n "$CLUSTER_NAMESPACE" -o custom-columns=PHASE:.status.process.status,MANAGED:.spec.managedLifecycle \
    | tr -s ' ' | grep -xF 'Completed true' | wc -l
}

enable_cluster_cron_schedule() {
  # Sets the value At every minute.
  wait_until kubectl patch sgcluster -n "$CLUSTER_NAMESPACE" "$CLUSTER_NAME" --type json \
    --patch '[{"op":"replace","path":"/spec/configurations/backups/0/cronSchedule","value":"*/1 * * * *"}]'
}

disable_cluster_cron_schedule() {
  # Sets the value At 05:00 on day-of-month 31 in February.
  wait_until kubectl patch sgcluster -n "$CLUSTER_NAMESPACE" "$CLUSTER_NAME" --type json \
    --patch '[{"op":"replace","path":"/spec/configurations/backups/0/cronSchedule","value":"0 5 31 2 *"}]'
}
